#include "udp_common.hpp"
#include <thread>
#include <sstream>
#include <unordered_map>
#include <mutex>
#include <chrono>
#define MAX_RETRANSMITS 5  // 全局最大重传次数
// 解决UDP的数据包丢失
// 1 在每个数据包中添加序列号。
// 2 发送数据包后等待服务器的确认（ACK）。
// 3 如果在超时时间内没有收到确认，重新发送数据包。
struct Message
{
    int seqNum; // 序列号
    std::string content;
};
class UdpClient
{
public:
    UdpClient(std::string ip, int port) : ip_(ip), port_(port), seqNum_(0)
    {
        sock_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock_ < 0)
        {
            perror("socket");
            exit(1);
        }

        // 目标地址
        peer_.sin_family = AF_INET;
        peer_.sin_port = htons(port_);
        inet_pton(AF_INET, ip_.c_str(), &peer_.sin_addr);
    }

    void run()
    {
        std::cout << "[UDP Client] send to " << ip_ << ":" << port_
                  << ", type /quit to exit\n";

        // 接收线程
        std::thread recv_thr(&UdpClient::recvLoop, this);
        
        // 主线程：键盘输入 -> 发送
        std::string line;
        while (std::getline(std::cin, line))
        {
            if (line == "/quit")
                break;  
            Message msg{seqNum_++, line};
            sendWithAck(msg);
        }
           running_ = false;
        close(sock_);
        recv_thr.join();
    }

private:


        void recvLoop()    // 接收 ACK 并调整窗口（AIMD）
    {
        char buf[BUF_SIZE];//服务端发送给客户端的数据，服务端返回的 ACK（确认报文）
        fd_set readfds;
        struct timeval tv;

        while (running_)
        {
            // 设置超时时间为500ms，定期检查超时
            tv.tv_sec = 0;
            tv.tv_usec = 500000; // 500毫秒

            FD_ZERO(&readfds);
            FD_SET(sock_, &readfds);

            // 使用select同时等待数据和超时
            int result = select(sock_ + 1, &readfds, NULL, NULL, &tv);

            if (result > 0 && FD_ISSET(sock_, &readfds))
            {
                // 有数据到来（收到服务器的ACK）
                socklen_t len = sizeof(peer_);
                ssize_t n = recvfrom(sock_, buf, sizeof(buf) - 1, 0,
                                    (sockaddr *)&peer_, &len);
                if (n <= 0)
                    break;
                buf[n] = '\0';
                
                if (strncmp(buf, "ACK:", 4) == 0)
                {
                    //提取 ACK 确认的序列号
                    //buf + 4 是指针运算，用于跳过 ACK 报文中的固定前缀 "ACK:"，提取后续的序列号。
                    int ack = std::stoi(buf + 4);
                    std::cout << "[Client] Received ACK for " << ack << "\n";
                    
                    if (ack >= baseSeq_)
                    {
                        baseSeq_ = ack + 1; // 滑动窗口，确认ack及之前的所有包
                        // 移除已确认的包
                        auto it = sent_.begin();
                        while (it != sent_.end())
                        {
                            if (it->first < baseSeq_)
                                it = sent_.erase(it);
                            else
                                ++it;
                        }
                        
                        // 拥塞控制：慢启动或拥塞避免
                        if (cwnd_ < ssthresh_)
                            cwnd_++; // 慢启动：指数增长
                        else
                            cwnd_ += 1.0 / cwnd_; // 拥塞避免：线性增长
                        
                        std::cout << "[Client] Updated cwnd: " << cwnd_ 
                                  << ", ssthresh: " << ssthresh_ << "\n";
                        
                        sendWindow(); // 继续发送窗口内的包
                    }
                }
            }
            else if (result == 0)
            {
                // 超时，检查是否有需要重传的包
                checkTimeout();
            }
        }
    }


    void sendWithAck(const Message &msg)
    {
        const int maxRetries = 3;
        const int timeout = 1000; // 1秒超时
        struct timeval tv;
        tv.tv_sec = timeout / 1000;
        tv.tv_usec = (timeout % 1000) * 1000;

        std::string message;
        {                                            // 作用域：限制 oss 的生命周期，避免不必要的内存占用
            std::ostringstream oss;                  // 创建一个字符串输出流对象
            oss << msg.seqNum << ":" << msg.content; // 向流中写入数据
            message = oss.str();                     // 提取流中的内容，转换为 std::string
        }

        for (int retry = 0; retry < maxRetries; ++retry)
        {
            // 1. 发送消息到服务器
            sendto(
                sock_,              // UDP套接字
                message.c_str(),    // 要发送的消息内容
                message.size(),     // 消息长度
                0,                  // 标志位（0表示默认）
                (sockaddr *)&peer_, //客户端的 IP+端口 由操作系统内核自动完成，服务端完全不用显式传入客户端的IP+端口
                sizeof(peer_)       // 地址结构体大小
            );
            // 准备等待服务器回应（使用select实现超时等待）
            fd_set readfds;          // 文件描述符集合（用于监听可读事件）
            FD_ZERO(&readfds);       // 清空集合
            FD_SET(sock_, &readfds); // 将UDP套接字加入集合（监听其是否有数据到来）
            // 3. 调用select等待数据，超时时间由tv指定
            int result = select(
                sock_ + 1,  // 监听的最大文件描述符+1
                &readfds,   // 监听可读事件的集合
                NULL, NULL, // 不监听可写和异常事件
                &tv         // 超时时间（超过这个时间没收到数据就触发超时）
            );
            // 4. 根据select的结果处理
            if (result > 0) // 有数据到来（收到服务器的ACK）
            {
                char buf[BUF_SIZE]; // 存储接收的ACK数据
                socklen_t len = sizeof(peer_);
                // 接收服务器回应
                recvfrom(
                    sock_,              // UDP套接字
                    buf,                // 接收缓冲区
                    BUF_SIZE,           // 缓冲区大小
                    0,                  // 标志位
                    (sockaddr *)&peer_, // 用于获取发送方（服务器）的地址
                    &len                // 地址结构体大小（传入传出参数）
                );
                std::cout << "[Client] Received ACK from server\n";
                return; // 收到ACK，成功退出
            }
            else // 超时（result=0）或出错（result=-1），重试
            {
                std::cout << "[Client] Timeout, retrying...\n";
            }
        }
        std::cout << "[Client] Failed to send message after " << maxRetries << " retries\n";
    }



//   // 修改后的sendWithAck：将消息加入缓冲区并触发发送，依赖全局超时重传
//     void sendWithAck(const Message &msg)
//     {
//         std::lock_guard<std::mutex> lock(mtx_);  // 加锁保护共享数据
        
//         // 1. 将消息存入缓冲区，记录初始重传次数
//         buffer_[msg.seqNum] = msg.content;
//         retransmitCount_[msg.seqNum] = 0;  // 初始化重传计数
        
//         std::cout << "[Client] Added message " << msg.seqNum 
//                   << " to buffer, preparing to send\n";
        
//         // 2. 触发窗口发送（会发送包括当前消息在内的窗口内所有包）
//         sendWindow();
//     }







            void sendWindow()
    {
        // 发送窗口内未发送的包
        int nextSeq = baseSeq_;
        while (nextSeq < baseSeq_ + cwnd_ && nextSeq < seqNum_)
        {
            if (sent_.find(nextSeq) == sent_.end())
            {
                // 构造数据包：序列号:内容
                std::string pkt = std::to_string(nextSeq) + ":" + buffer_[nextSeq];
                sendto(sock_, pkt.c_str(), pkt.size(), 0,
                       (sockaddr *)&peer_, sizeof(peer_));
                
                std::cout << "[Client] Sent packet " << nextSeq << "\n";
                sent_[nextSeq] = std::chrono::steady_clock::now();
            }
            nextSeq++;
        }
    }





//  void sendWindow()
//     {
//         std::lock_guard<std::mutex> lock(mtx_);  // 加锁保护共享数据
        
//         // 发送窗口内未发送的包（窗口范围：[baseSeq_, baseSeq_ + cwnd_)）
//         //nextSeq:下一个将要发送的数据包的序号（即待发送数据的起始序号）
//         //baseSeq_:发送窗口的 “基序号”，表示当前未被接收方确认的第一个数据包的序号（即最早未确认数据的起始位置）
//         int nextSeq = baseSeq_;
            
//         while (nextSeq < baseSeq_  + cwnd_ // 控制拥塞，不超过窗口限制
//             && nextSeq < seqNum_)//确保数据已准备好，不发送无效数据
//         {
//             // 如果包未发送，或需要重传（且未超过最大重传次数）
//             if ((sent_.find(nextSeq) == sent_.end() || 
//                  retransmitCount_[nextSeq] > 0) && 
//                 retransmitCount_[nextSeq] < MAX_RETRANSMITS)
//             {
//                 // 构造数据包：序列号:内容
//                 std::string pkt = std::to_string(nextSeq) + ":" + buffer_[nextSeq];
//                 sendto(sock_, pkt.c_str(), pkt.size(), 0,
//                        (sockaddr *)&peer_, sizeof(peer_));
                
//                 // 更新发送时间和重传计数
//                 sent_[nextSeq] = std::chrono::steady_clock::now();
//                 retransmitCount_[nextSeq]++;
                
//                 std::cout << "[Client] Sent packet " << nextSeq 
//                           << " (retry: " << retransmitCount_[nextSeq] - 1 << ")\n";
//             }
//             nextSeq++;
//         }
//     }





    // 超时重传
    void checkTimeout()
    {
        auto now = std::chrono::steady_clock::now();
        for (auto &[seq, t] : sent_)
        {
            if (std::chrono::duration_cast<std::chrono::milliseconds>(now - t).count() > 1000)
            {
                // 丢包：乘性减
                ssthresh_ = std::max(cwnd_ / 2, 1);
                cwnd_ = 1;
                std::cout << "[Client] timeout, cwnd -> " << cwnd_ << "\n";
                // 重传 baseSeq_
                std::string pkt = std::to_string(baseSeq_) + ":" + buffer_[baseSeq_];
                sendto(sock_, pkt.c_str(), pkt.size(), 0,
                       (sockaddr *)&peer_, sizeof(peer_));
                sent_[baseSeq_] = now;
                break;
            }
        }
    }
  std::mutex mtx_;  // 保护共享数据的互斥锁
    int sock_;
    std::string ip_;
    int port_;
    sockaddr_in peer_;
    int seqNum_; // 序列号
   bool running_;   // 线程运行标志
    // 用滑动窗口解决拥塞控制
    int cwnd_ = INIT_CWND;                                                // 当前窗口
    int ssthresh_ = MAX_WINDOW / 2;                                       // 阈值
    int baseSeq_ = 0;                                                     // 已确认的最小序号
    std::unordered_map<int, std::chrono::steady_clock::time_point> sent_; // 序号->发送时间
    std::unordered_map<int, std::string> buffer_;                         // 序号->内容
     std::unordered_map<int, int> retransmitCount_;                        // 序号->重传次数
};

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cerr << "Usage: ./udp_client <server_ip> <port>\n";
        return 1;
    }

    // 客户端自己的ip和port为什么不需要传入参数，客户端自己不需要ip和端口号吗?
    // 客户端当然需要自己的 IP 和端口号,IP 由操作系统自动绑定,端口由操作系统自动分配
    // 唯一性由五元组保证
    // 数据包实际上带着五元组：
    //(源IP, 源端口, 目的IP, 目的端口, 协议)
    // 即使多台客户端都用同一个临时端口，只要 源 IP 不同（或者 IP 相同但端口不同），服务器也能区分。

    // ip 和 port 是目标主机的地址,这个ip和端口一般是服务端的 IP 和端口
    UdpClient cli(argv[1], std::stoi(argv[2]));
    cli.run();
    return 0;
}
